#include "udp_client.h"


UdpClient::UdpClient(/* args */)
{
    udp_socket_ = -1;
    m_event_socket_ = eventfd(0, 0);
    m_socket_is_open_ = false;
    m_process_thread_start_ = false;
}

UdpClient::~UdpClient()
{
    printf("%s\n", __FUNCTION__);
}

bool UdpClient::Init(std::string ip, uint16_t port)
{
    m_ip_ = ip;
    m_port_ = port;

    // int ret = -1;
    //server addr
    remote.sin_family = AF_INET;
    remote.sin_port = htons((u_short)m_port_);
    inet_pton(AF_INET, m_ip_.c_str(), &remote.sin_addr.s_addr);

    //socket
    udp_socket_ = socket(PF_INET, SOCK_DGRAM, 0);

    if(udp_socket_ <= 0){
        perror("udp socket fail!!!\n");
        return false;
    }

    //SO_REUSEADDR            许完全重复的捆绑
    int socketoptvalue = 1;
    setsockopt(udp_socket_, SOL_SOCKET, SO_REUSEADDR, (const char*)&socketoptvalue, sizeof(int));

    //SO_RCVBUF              发送缓冲区
    socketoptvalue = 128 * 1024;
    setsockopt(udp_socket_, SOL_SOCKET, SO_RCVBUF, (const char*)(&socketoptvalue), sizeof(int));
    setsockopt(udp_socket_, SOL_SOCKET, SO_SNDBUF, (const char*)&socketoptvalue, sizeof(int));

    socketoptvalue = 3;
    setsockopt(udp_socket_, SOL_SOCKET, SO_RCVTIMEO, (const char*)(&socketoptvalue), sizeof(int));
    setsockopt(udp_socket_, SOL_SOCKET, SO_SNDTIMEO, (const char*)(&socketoptvalue), sizeof(int));

    //设置为非阻塞
    if (fcntl(udp_socket_, F_SETFL, O_NONBLOCK | FASYNC) < 0) {
        perror("non-block");
        return false;
    }
    m_socket_is_open_ = true;
    return true;

}

bool UdpClient::Start()
{
    printf("%s\n", __FUNCTION__);
    m_process_thread_start_ = true;
    process_thread_.reset(new std::thread([&] (){
        while (m_process_thread_start_)
        {
            /* code */
            if(!ProcessLoop()){
                std::this_thread::sleep_for(std::chrono::milliseconds(100));
            }
                
        }

        printf("this thread end!\n");
        
    }));
    return true;
}


bool UdpClient::Stop()
{
    printf("%s\n", __FUNCTION__);
    m_process_thread_start_ = false;
    if(process_thread_->joinable())
        process_thread_->join();
    if(m_socket_is_open_ ){
        close(udp_socket_);
        udp_socket_ = -1;
        m_socket_is_open_ = false;
    }
    if(m_event_socket_)
        close(m_event_socket_);

    return true;       
}


bool UdpClient::SendData(const uint8_t *data, int32_t len)
{
    if(!m_socket_is_open_)
        return false;
    
    int32_t size = send_data_buf_.size();
    DataPacket tmp;
    tmp.data = data;
    tmp.len = len;
    tx_buf_mutex_.lock();
    send_data_buf_.push_back(tmp);
    tx_buf_mutex_.unlock();

    if(size == 0){
        uint64_t u = 1;
        ssize_t v = write(m_event_socket_, &u, sizeof(uint64_t));
        if (v < 0)
            return false;
    }

    return true;
}

bool UdpClient::ProcessLoop()
{
    fd_set rfds,wfds;
    int32_t maxfd = 0;

    FD_ZERO(&rfds);
    FD_ZERO(&wfds);

    if(maxfd < udp_socket_)
        maxfd = udp_socket_;
    
    FD_SET(udp_socket_, &rfds);

    if(!send_data_buf_.empty()){
        FD_SET(udp_socket_, &wfds);
    }

    FD_SET(m_event_socket_,&rfds);
    if(maxfd < m_event_socket_)
        maxfd = m_event_socket_;

    struct timeval tv = { 0, 500 * 1000 };
    int ret = select(maxfd+1, &rfds, &wfds, NULL, &tv);
    uint8_t tmpBuf[1024];
    if(ret > 0){
        if(FD_ISSET(udp_socket_, &rfds)){
            int rx_n = recvfrom(udp_socket_, (uint8_t *)tmpBuf, sizeof(tmpBuf), 0, NULL, NULL);
            if(rx_n > 0){
                //todo callback;
                if(callback_)
                    callback_(tmpBuf, rx_n);
            }else{
                printf("read error!\n");
                FD_CLR(udp_socket_, &rfds);
                return false;
            }
        }

        if(FD_ISSET(udp_socket_, &wfds)){
            DataPacket tmp;
            tx_buf_mutex_.lock();
            tmp = send_data_buf_.front();
            send_data_buf_.pop_front();
            tx_buf_mutex_.unlock();
            if (tmp.len > 0) {
                int v = sendto(udp_socket_, tmp.data, (size_t)tmp.len, 0, (struct sockaddr*)&remote, sizeof(sockaddr_in));
                printf("client send len: %d\n", v);
                if (v < 0) ret = -1;
            }
        }

        if(FD_ISSET(m_event_socket_, &rfds)){
            ssize_t v = read(m_event_socket_, tmpBuf, sizeof(uint64_t));
            if (v < 0) ret = -1;
        }
    }

    return ret >= 0;

}